/******************************************************************************
ISYS 1078 / 1079 — Information Retrieval
Assignment #1

Student Name: [ Rui, Wang ]
Student ID: [ 3171459 ]
CSIT username: [ rwang ]
*******************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <math.h>
#include "searchFunc.h"


int BuildSearchMap(HashTable *HT, DocMap *DM, FILE *fp_lex, FILE *fp_map)
{
  	HashTableInit(HT, fp_lex);
	BuildHashTable(HT, fp_lex);

    DocMapInit(DM, fp_map);
	BuildDocMap(DM, fp_map);
	return 0;
}


/*
	calculate the min prime key for hashtable size 
	reference: chenguang zhou's oiginal idea and formular code
*/
int PrimeSet ( int x )
{
   if(x % 2 == 0)
      x++; 
   while(!IsPrime(x))
   {
      x += 2; /*only judge odd number*/
   }
   return x;
}

/*
	calculate the min prime key for hashtable size 
	reference: chenguang zhou's idea and oiginal formular
*/
int IsPrime(int x) 
{/*judge all the odd number between 3 and square root of x*/ 
   int i;
   for (i = 3; i*i <= x; i += 2)
      if (x % i == 0) 
         return 0; 
   return 1; 
}

int Counter(FILE *fp, int mode)
{
	int count;
	Term* currTerm;
	Doc* currDoc;
	
	currTerm = NULL;
	currDoc = NULL;
	count =0;

	/* Term mode  0
	   Doc  mode  1 */
	if(mode == 0)
	{   
		currTerm = malloc(sizeof(Term));	
		while(fread(currTerm, sizeof(Term), 1, fp) == 1)
			count++;
		free(currTerm);
	}
	else
	{
		currDoc = malloc(sizeof(Doc));
		while(fread(currDoc, sizeof(Doc), 1, fp) == 1)
			count++;
		free(currDoc);
	}	
	
	rewind(fp);
	return count;
}

/*global variable for two hashtables size*/
int DMSIZE =0;
int HTSIZE =0;

int HashTableInit(HashTable *HT, FILE *fp)
{
	int sum, index;
	
	sum = Counter(fp,0);
/*	printf("Term-Count %d", sum);*/
	
	/*cal the size of hashtable*/
	HTSIZE = PrimeSet(ceil(sum));
	
	if((HT->head =malloc(HTSIZE * sizeof(BucketNode))) == NULL)
	{
		fprintf(stderr,"Hash Table memory allocation failed!\n");
		return -1;
	}
	
	for(index = 0; index < HTSIZE; index++)
		HT->head[index] = NULL;
		
	return 0;
}

int DocMapInit(DocMap *DM, FILE *fp)
{
	int sum, index;
	sum = Counter(fp,1);
/*	printf("Doc-Count %d", sum);*/
	
	/*cal the size of hashtable*/
	DMSIZE = PrimeSet(ceil(sum));
	
	if((DM->head =malloc(DMSIZE * sizeof(MapNode))) == NULL)
	{
		fprintf(stderr,"Hash Table memory allocation failed!\n");
		return -1;
	}
	
	for(index = 0; index < DMSIZE; index++)
		DM->head[index] = NULL;
		
	return 0;
}

/*
	Reference: the main part of this HashFunc is from BKDRHash code
	http://www.partow.net/programming/hashfunctions/
*/
int HashFunc(char *str, int size)
{
   unsigned int hash = 0 ;
	int i = 0;
   while( *str)
   {
	   	if((i & 1) == 0)
       		hash ^= ((hash << 7 ) + ( *(str++) ) + (hash >> 3));
		else
			hash ^= ( ~ ((hash << 11 ) ^ ( * str ++ ) ^ (hash >> 5 )));
		
		i++;
   } 

   return  (hash % size );
}


void BucketInsert(HashTable *HT, Term *termNode)
{
	BucketList curr, prev, new;
	int hashValue;
	
	if((new = malloc(sizeof(BucketNode))) == NULL)
	{
		fprintf(stderr,"Memory Allocation Failed! (BucketNode insert)\n");
		return;
	}	
	
	new->next = NULL;

	new->term = malloc((strlen(termNode->term))*sizeof(char));
	strcpy(new->term, termNode->term);
	new->ppos = termNode->ppos;
	
	hashValue = HashFunc(new->term, HTSIZE);
	curr = HT->head[hashValue];
	prev = NULL;
	
	while(curr != NULL)
	{
		prev = curr;
		curr = curr->next;
	}
	
	if(prev == NULL)
		HT->head[hashValue] = new;
	else
		prev->next = new;

}

int BuildHashTable(HashTable *HT, FILE *fp)
{
	Term *curr;
	curr = malloc(sizeof(Term));
	
	while(fread(curr, sizeof(Term), 1, fp) ==1)
	{
		BucketInsert(HT, curr);
	}
	
	free(curr);	
	return 0;
}


int BuildDocMap(DocMap *DM, FILE *fp)
{
	Doc *curr;
	curr = malloc(sizeof(Doc));

	while(fread(curr, sizeof(Doc), 1, fp) ==1)
	{
		DocBucketInsert(DM, curr);
	}
	
	free(curr);
	return 0;
}

void DocBucketInsert(DocMap *DM, Doc *docNode)
{
	MapList curr, prev, new;
	char id[DOC_LEN];
	int hashValue;
	
	if((new = malloc(sizeof(MapNode))) == NULL)
	{
		fprintf(stderr,"Memory Allocation Failed! (BucketNode insert)\n");
		return;
	}	
	
	new->next = NULL;

	new->docNum = malloc((strlen(docNode->docNum)+1)*sizeof(char));
	ToUpperCase(docNode->docNum);
	strcpy(new->docNum, docNode->docNum);
	new->docId = docNode->docId;

    /*convert docId to string type for hashing*/
	sprintf(id, "%d", new->docId);
	hashValue = HashFunc(id, DMSIZE);

	curr = DM->head[hashValue];
	
	prev = NULL;
	
	while(curr != NULL)
	{
		prev = curr;
		curr = curr->next;
	}
	
	if(prev == NULL)
		DM->head[hashValue] = new;
	else
		prev->next = new;
	
}

int SearchIvtlists(int ppos, DocMap *DM, FILE *fp)
{
	int freq_t, docId, freq_dt, pos;
	int i, j;
	int hashValue;
	MapList currNode;
	char id[DOC_LEN];
	
	/*set posting pointer*/
	fseek(fp, ppos, SEEK_SET);
	
	if(fread(&freq_t, sizeof(int), 1, fp) == 1)
	{
		fprintf(stdout,"%d\n", freq_t);
		
		for(i = 0; i< freq_t; i++)
		{
			fread(&docId, sizeof(int), 1, fp);
			sprintf(id, "%d", docId);
		
			hashValue = HashFunc(id, DMSIZE);
			currNode = DM->head[hashValue];
			
		  	while(currNode != NULL)
			{
				if(currNode->docId == docId)
				{
					fprintf(stdout, "%s  ", currNode->docNum);
					break;
				}
				currNode = currNode->next;
			}
			
			fread(&freq_dt, sizeof(int), 1, fp);
			fprintf(stdout, " %d   ", freq_dt);

			/*print sub posting info*/
			for(j = 0; j< freq_dt; j++)
			{
				fread(&pos, sizeof(int), 1, fp);
				fprintf(stdout, " %d ", pos);
			}

			fprintf(stdout, "\n");
		}
	
	}
	else
		return -1;
	

	return 0;
}


int Query(char* qterm, HashTable *HT, DocMap *DM, FILE *fp)
{
	int hashValue;
	BucketList currNode;
	
	hashValue = HashFunc(qterm, HTSIZE);
	
	currNode = HT->head[hashValue];

	while(currNode != NULL)
	{	
		if(strcmp(currNode->term, qterm) == 0)
		{			
			/*print query iterm*/
			fprintf(stdout, "\n%s\n", currNode->term);
			
			/*search invlists file, get details*/
			SearchIvtlists(currNode->ppos, DM, fp);

			/*reset the fp_ivt to the start of file*/
			rewind(fp);
			return 0;
		}
		
		currNode = currNode->next;
	}
	
	fprintf(stdout,"\nCAUTION !\n%s  NOT FOUND ! \n%d\n", qterm, 0);
	return -1;
}

void ToUpperCase(char* str)
{
	int p = 0;

	while(str[p] != '\0')
	{
		if(str[p] >= 'a' && str[p] <= 'z')
		{
			str[p] -= 32;
		}
		p++;
	}
}

void DestroyHashTable(HashTable *HT, DocMap *DM)
{
	HashTableFree(HT);
	DocMapFree(DM);
}
void HashTableFree(HashTable *HT)
{
	int i;
	
	for(i = 0; i< HTSIZE; i++)
		BucketListFree(HT->head[i]);
		
	free(HT->head);
}
void BucketListFree(BucketList curr)
{
	BucketList next;
	
	while(curr != NULL)
	{
		next = curr->next;
		free(curr->term);
		free(curr);
		curr = next;
	}
}
void DocMapFree(DocMap *DM)
{
	int i;
	
	for(i = 0; i< DMSIZE; i++)
		DocBucketListFree(DM->head[i]);
		
	free(DM->head);
}
void DocBucketListFree(MapList curr)
{
	MapList next;
	
	while(curr != NULL)
	{
		next = curr->next;
		free(curr->docNum);
		free(curr);
		curr = next;
	}
}
